package org.jawa.database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.jawa.Constants;
import org.logicalcobwebs.proxool.ProxoolException;
import org.logicalcobwebs.proxool.ProxoolFacade;

/**
 * 提供Proxool的数据库连接池
 */
public class ProxoolConnectionProvider implements ConnectionProvider {

	private Properties settings;
	protected DataSource dataSource;
	private String driver;
	private String serverURL;
	private String proxoolURL;
	private String username;
	private String password;
	private int minConnections = 3;
	private int maxConnections = 10;
	private int activeTimeout = 900000; // 15 minutes in milliseconds
	/**
	 * Maximum time a connection can be open before it's reopened (in days)
	 */
	private double connectionTimeout = 0.5;

	/**
	 * MySQL doesn't currently support Unicode. However, a workaround is
	 * implemented in the mm.mysql JDBC driver. Setting the Jawa property
	 * database.mysql.useUnicode to true will turn this feature on.
	 */
	private boolean mysqlUseUnicode;

	public ProxoolConnectionProvider() {
		loadProperties();
		dataSource = new JawaDataSource(this);
	}

	@Override
	public void destroy() {
		settings = null;
		if (dataSource != null){
			try {
				ProxoolFacade.shutdown(3);
			} catch (Exception e) {
				// Log.error(e);
				e.printStackTrace();
			}
			dataSource = null;
		}
	}

	@Override
	public Connection getConnection() throws SQLException {
		Connection con;
		try {
			con = DriverManager.getConnection("proxool.jawashop");
		} catch (Exception e) {
			throw new SQLException(
					"ProxoolConnectionProvider: Unable to find driver: " + e);
		}
		return con;
	}

	@Override
	public DataSource getDataSource() {
		return dataSource;
	}

	@Override
	public boolean isPooled() {
		return true;
	}

	@Override
	public void restart() {
		destroy();
		loadProperties();
		start();
	}

	@Override
	public void start() {
		proxoolURL = "proxool.jawashop:" + getDriver() + ":" + getServerURL();
		settings = new Properties();
		settings.setProperty("proxool.maximum-activetime", Integer
				.toString(activeTimeout));
		settings.setProperty("proxool.maximum-connection-count", Integer
				.toString(getMaxConnections()));
		settings.setProperty("proxool.minimum-connection-count", Integer
				.toString(getMinConnections()));
		settings.setProperty("proxool.maximum-connection-lifetime", Integer
				.toString((int) (86400000 * getConnectionTimeout())));
		settings.setProperty("user", getUsername());
		settings.setProperty("password", (getPassword() != null ? getPassword()
				: ""));
		try {
			ProxoolFacade.registerConnectionPool(proxoolURL, settings);
		} catch (ProxoolException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 保存数据库默认的连接池配置
	 */
	private void saveProperties() {

		Constants.setXMLProperty("database.proxool.driverClassName", driver);
		Constants.setXMLProperty("database.proxool.serverURL",
				serverURL);
		Constants.setXMLProperty("database.proxool.username", username);
		Constants.setXMLProperty("database.proxool.password", password);

		Constants.setXMLProperty("database.proxool.minConnections",
				Integer.toString(minConnections));
		Constants.setXMLProperty("database.proxool.maxConnections",
				Integer.toString(maxConnections));
		Constants.setXMLProperty("database.proxool.connectionTimeout",
				Double.toString(connectionTimeout));
	}

	/**
	 * 读取数据库默认的连接池配置
	 */
	private void loadProperties() {
		driver = Constants.getXMLProperty("database.proxool.driverClassName");
		serverURL = Constants
				.getXMLProperty("database.proxool.serverURL");
		username = Constants
				.getXMLProperty("database.proxool.username");
		password = Constants
				.getXMLProperty("database.proxool.password");
		String minCons = Constants
				.getXMLProperty("database.proxool.minConnections");
		String maxCons = Constants
				.getXMLProperty("database.proxool.maxConnections");
		String conTimeout = Constants
				.getXMLProperty("database.proxool.connectionTimeout");
		// See if we should use Unicode under MySQL
		mysqlUseUnicode = Boolean.valueOf(Constants
				.getXMLProperty("database.mysql.useUnicode"));
		try {
			if (minCons != null) {
				minConnections = Integer.parseInt(minCons);
			}
			if (maxCons != null) {
				maxConnections = Integer.parseInt(maxCons);
			}
			if (conTimeout != null) {
				connectionTimeout = Double.parseDouble(conTimeout);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 返回数据库连接池 JDBC driver 的名称 例如: com.mysql.jdbc.Driver
	 * 
	 * @return the JDBC driver classname.
	 */
	public String getDriver() {
		return driver;
	}

	/**
	 * 返回数据库连接池 JDBC driver 的名称 例如: com.mysql.jdbc.Driver
	 * 
	 * @param driver
	 *            the fully qualified JDBC driver name.
	 */
	public void setDriver(String driver) {
		this.driver = driver;
	}

	/**
	 * Returns the JDBC connection URL used to make database connections.
	 * 
	 * @return the JDBC connection URL.
	 */
	public String getServerURL() {
		return serverURL;
	}

	/**
	 * Sets the JDBC connection URL used to make database connections.
	 * 
	 * @param serverURL
	 *            the JDBC connection URL.
	 */
	public void setServerURL(String serverURL) {
		this.serverURL = serverURL;
		saveProperties();
	}

	/**
	 * Returns the username used to connect to the database. In some cases, a
	 * username is not needed so this method will return null.
	 * 
	 * @return the username used to connect to the datbase.
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * Sets the username used to connect to the database. In some cases, a
	 * username is not needed so null should be passed in.
	 * 
	 * @param username
	 *            the username used to connect to the database.
	 */
	public void setUsername(String username) {
		this.username = username;
		saveProperties();
	}

	/**
	 * Returns the password used to connect to the database. In some cases, a
	 * password is not needed so this method will return null.
	 * 
	 * @return the password used to connect to the database.
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * Sets the password used to connect to the database. In some cases, a
	 * password is not needed so null should be passed in.
	 * 
	 * @param password
	 *            the password used to connect to the database.
	 */
	public void setPassword(String password) {
		this.password = password;
		saveProperties();
	}

	/**
	 * Returns the minimum number of connections that the pool will use. This
	 * should probably be at least three.
	 * 
	 * @return the minimum number of connections in the pool.
	 */
	public int getMinConnections() {
		return minConnections;
	}

	/**
	 * Sets the minimum number of connections that the pool will use. This
	 * should probably be at least three.
	 * 
	 * @param minConnections
	 *            the minimum number of connections in the pool.
	 */
	public void setMinConnections(int minConnections) {
		this.minConnections = minConnections;
		saveProperties();
	}

	/**
	 * Returns the maximum number of connections that the pool will use. The
	 * actual number of connections in the pool will vary between this value and
	 * the minimum based on the current load.
	 * 
	 * @return the max possible number of connections in the pool.
	 */
	public int getMaxConnections() {
		return maxConnections;
	}

	/**
	 * Sets the maximum number of connections that the pool will use. The actual
	 * number of connections in the pool will vary between this value and the
	 * minimum based on the current load.
	 * 
	 * @param maxConnections
	 *            the max possible number of connections in the pool.
	 */
	public void setMaxConnections(int maxConnections) {
		this.maxConnections = maxConnections;
		saveProperties();
	}

	/**
	 * Returns the amount of time between connection recycles in days. For
	 * example, a value of .5 would correspond to recycling the connections in
	 * the pool once every half day.
	 * 
	 * @return the amount of time in days between connection recycles.
	 */
	public double getConnectionTimeout() {
		return connectionTimeout;
	}

	/**
	 * Sets the amount of time between connection recycles in days. For example,
	 * a value of .5 would correspond to recycling the connections in the pool
	 * once every half day.
	 * 
	 * @param connectionTimeout
	 *            the amount of time in days between connection recycles.
	 */
	public void setConnectionTimeout(double connectionTimeout) {
		this.connectionTimeout = connectionTimeout;
		saveProperties();
	}

	public boolean isMysqlUseUnicode() {
		return mysqlUseUnicode;
	}

}
